package repository

import (
	"invitation/internal/model"
	"invitation/internal/repository/cache"

	"github.com/rs/zerolog/log"
	"gorm.io/gorm"
)

type GameInfoRepository interface {
	Create(gameInfo *model.GameInfo) (uint, error)
	FindListAll() ([]model.GameInfo, error)
	FindOneByName(name string) (*model.GameInfo, error)
	FindOneByID(id uint) (*model.GameInfo, error)

	FindListByIDs(ids []uint) ([]model.GameInfo, error)
	Update(gameInfo *model.GameInfo) error
	Delete(id uint) error
}

type gameInfoRepository struct {
	*BaseRepository
}

func newGameInfoRepository(db *gorm.DB) *gameInfoRepository {
	return &gameInfoRepository{
		BaseRepository: newBaseRepository(db),
	}
}

func (gir *gameInfoRepository) Create(gameInfo *model.GameInfo) (uint, error) {
	err := gir.GetDB().Create(gameInfo).Error
	if err != nil {
		return 0, err
	}
	return gameInfo.ID, nil
}
func (gir *gameInfoRepository) FindListAll() ([]model.GameInfo, error) {
	var gameInfoList []model.GameInfo
	err := gir.GetDB().Find(&gameInfoList).Error
	if err != nil {
		return nil, err
	}
	return gameInfoList, nil
}

func (gir *gameInfoRepository) FindOneByName(name string) (*model.GameInfo, error) {
	var gameInfo model.GameInfo
	err := gir.GetDB().Where("name = ?", name).First(&gameInfo).Error
	if err != nil {
		return nil, err
	}
	return &gameInfo, nil
}

func (gir *gameInfoRepository) FindOneByID(id uint) (*model.GameInfo, error) {
	var gameInfo model.GameInfo
	err := gir.GetDB().Where("id = ?", id).First(&gameInfo).Error
	if err != nil {
		return nil, err
	}
	return &gameInfo, nil
}
func (gir *gameInfoRepository) FindListByIDs(ids []uint) ([]model.GameInfo, error) {
	var gameInfoList []model.GameInfo
	err := gir.GetDB().Where("id IN (?)", ids).Find(&gameInfoList).Error
	if err != nil {
		return nil, err
	}
	return gameInfoList, nil
}
func (gir *gameInfoRepository) Update(gameInfo *model.GameInfo) error {
	// 1. 获取旧数据，用于零值比较
	var oldGameInfo model.GameInfo

	updateData := make(map[string]interface{})

	if gameInfo.Name != "" {
		updateData["name"] = gameInfo.Name
	}

	// 检查 Desc
	if gameInfo.Desc != "" {
		updateData["desc"] = gameInfo.Desc
	} else {
		// 如果新值为空，则保留旧值（或者不把它加入 updateData）
		// GORM 的 Updates 方法只会更新 map 中存在的字段
	}

	// 检查 ProblemToSolve
	if gameInfo.ProblemToSolve != "" {
		updateData["problem_to_solve"] = gameInfo.ProblemToSolve // 注意使用数据库列名
	}

	// ID, CreatedAt, DeletedAt 不应在此处手动更新
	// UpdatedAt 会被 GORM 自动处理

	// 3. 执行更新
	// 如果 updateData 为空，则无需执行数据库操作
	if len(updateData) == 0 {
		return nil // 或者返回一个错误，表示没有提供任何有效更新字段
	}

	// 使用 Updates 方法，它会自动处理 UpdatedAt
	return gir.db.Model(&oldGameInfo).Where("id = ?", gameInfo.ID).Updates(updateData).Error
}

func (gir *gameInfoRepository) Delete(id uint) error {
	return gir.GetDB().Delete(&model.GameInfo{}, id).Error
}

type cachedGameInfoRepository struct {
	*BaseCachedRepository
}

func newCachedGameInfoRepository(db *gorm.DB, cache *cache.LRU, repoName string) *cachedGameInfoRepository {
	repo := newGameInfoRepository(db)
	return &cachedGameInfoRepository{
		BaseCachedRepository: newBaseCachedRepository(repo, cache, repoName),
	}
}

func (cgr *cachedGameInfoRepository) BaseRepo() *gameInfoRepository {
	return cgr.GetBaseRepo().(*gameInfoRepository)
}

var (
	gameInfoListAllKey = "list_all"
	gameInfoNameKey    = "name"
)

func (cgr *cachedGameInfoRepository) Create(gameInfo *model.GameInfo) (uint, error) {
	cgr.mutex.Lock()
	defer cgr.mutex.Unlock()
	id, err := cgr.BaseRepo().Create(gameInfo)
	if err != nil {
		return 0, err
	}
	if cgr.GetCache() == nil {
		return id, err
	}
	cacheKey := cgr.GenerateCacheKey(gameInfoListAllKey)
	cgr.GetCache().Remove(cacheKey)
	return id, err
}
func (cgr *cachedGameInfoRepository) FindListAll() ([]model.GameInfo, error) {
	if cgr.GetCache() == nil {
		// 如果没有缓存，直接使用基础实现
		return cgr.BaseRepo().FindListAll()
	}
	cacheKey := cgr.GenerateCacheKey(gameInfoListAllKey)

	if cache, ok := cgr.GetCache().Get(cacheKey); ok {
		if gameInfoList, ok := cache.([]model.GameInfo); ok {
			return gameInfoList, nil
		}
	}
	gameInfoList, err := cgr.BaseRepo().FindListAll()
	if err == nil {
		cgr.GetCache().Add(cacheKey, gameInfoList)
		log.Debug().Msg("add gameInfoList to cache")
	}
	return gameInfoList, err

}
func (cgr *cachedGameInfoRepository) FindOneByID(id uint) (*model.GameInfo, error) {
	if cgr.GetCache() == nil {
		return cgr.BaseRepo().FindOneByID(id)
	}
	cacheKey := cgr.GenerateCacheKey(gameInfoNameKey, id)
	if cache, ok := cgr.GetCache().Get(cacheKey); ok {
		if gameInfo, ok := cache.(*model.GameInfo); ok {
			return gameInfo, nil
		}
	}
	gameInfo, err := cgr.BaseRepo().FindOneByID(id)
	if err == nil {
		cgr.GetCache().Add(cacheKey, gameInfo)
	}
	return gameInfo, err
}

func (cgr *cachedGameInfoRepository) FindListByIDs(ids []uint) ([]model.GameInfo, error) {
	return cgr.BaseRepo().FindListByIDs(ids)
}
func (cgr *cachedGameInfoRepository) FindOneByName(name string) (*model.GameInfo, error) {
	if cgr.GetCache() == nil {
		// 如果没有缓存，直接使用基础实现
		return cgr.BaseRepo().FindOneByName(name)
	}
	cacheKey := cgr.GenerateCacheKey(gameInfoNameKey, name)
	if cache, ok := cgr.GetCache().Get(cacheKey); ok {
		if gameInfo, ok := cache.(*model.GameInfo); ok {
			return gameInfo, nil
		}
	}
	gameInfo, err := cgr.BaseRepo().FindOneByName(name)
	if err == nil {
		cgr.GetCache().Add(cacheKey, gameInfo)
	}
	return gameInfo, err

}

func (cgr *cachedGameInfoRepository) Update(gameInfo *model.GameInfo) error {
	cgr.mutex.Lock()
	defer cgr.mutex.Unlock()
	oldGameInfo, err := cgr.BaseRepo().FindOneByID(gameInfo.ID)
	if err != nil {
		return err
	}
	err = cgr.BaseRepo().Update(gameInfo)
	if err != nil {
		return err
	}

	if cgr.GetCache() == nil {
		return err
	}

	// 清理列表缓存
	cacheKey := cgr.GenerateCacheKey(gameInfoListAllKey)
	cgr.GetCache().Remove(cacheKey)
	log.Debug().Msg("remove game info list all cache")

	// 如果名称发生变化，清理旧名称缓存
	if oldGameInfo != nil && oldGameInfo.Name != gameInfo.Name {
		oldNameKey := cgr.GenerateCacheKey(gameInfoNameKey, oldGameInfo.Name)
		cgr.GetCache().Remove(oldNameKey)
	}

	// 更新新名称缓存
	newNameKey := cgr.GenerateCacheKey(gameInfoNameKey, gameInfo.Name)
	cgr.GetCache().Add(newNameKey, gameInfo)

	return nil
}
func (cgr *cachedGameInfoRepository) Delete(id uint) error {
	cgr.mutex.Lock()
	defer cgr.mutex.Unlock()
	err := cgr.BaseRepo().Delete(id)
	if err != nil {
		return err
	}
	if cgr.GetCache() == nil {
		return err
	}
	cacheKey := cgr.GenerateCacheKey(gameInfoListAllKey)
	cgr.GetCache().Remove(cacheKey)
	return err
}
func NewGameInfoRepositoryWithCache(db *gorm.DB, cache *cache.LRU, repoName string) Repository {
	return newCachedGameInfoRepository(db, cache, repoName)
}
